package core;
/**
 * @(#)Platform.java
 *
 * Platform application
 *
 * @author 
 * @version 1.00 2008/10/1             MAIN VERSION
 */
 
 //  WALL DEMO MOD!
 //  ingen autofire. Val f�r att skapa v�ggar inlagt i Attacks. Claymore och Back borttagna f�r snabbare val.
 

import graphics.ExplosionMainParticle;
import graphics.GTestFrame;
import graphics.ImageHandler;
import graphics.Particle;
import graphics.SpriteHandler;

import javax.swing.*;

import menu.ConfigurationMenu;
import menu.MainMenu;

import core.*;
import core.actions.*;
import core.gamemodes.FreeForAll;
import core.gamemodes.GameMode;
import core.gamemodes.Survival;
import core.gamemodes.Tutorial;
import core.mouse.OBCMouseListener;
import core.units.*;
import core.weapons.*;
import core.world.*;
import core.world.Map;

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.Timer;


/**
 * 
 * @author Anton
 *
 * The gamecore of One Button Commander
 *
 */
public class SingleButtonGame extends JFrame implements Runnable, KeyListener {
	
	private static final long serialVersionUID = 1L;
	final static double PI = 3.1415;
	enum GameState {MENU, INGAME, CONFIGURATION, VICTORYSCREEN};
	private GameState gameState = GameState.MENU;
	
	
	MainMenu mainMenu;
	ConfigurationMenu configurationMenu;
	private static SingleButtonGame me;
    
    //UI
    JPanel spelyta = new JPanel();
    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    //Gamerunning
    Thread th;
    //Graphics
    private Graphics dbg;
    private Image dbImage;

    public Vector<Unit> units = new Vector<Unit>(100);
    
    public final Color[] PLAYERCOLORS = {Color.green,Color.red,Color.blue,Color.cyan,Color.magenta,Color.yellow,Color.orange,Color.pink};

    public Vector<Player> players = new Vector<Player>();

    public Vector<Attack> attacks = new Vector<Attack>(150);
    
    public AbstractList<Particle> particles = new ArrayList<Particle>(15000);
    
    public Vector<Object> objectsToRemove = new Vector<Object>();
    
    public Map map;
    public GameMode gameMode;
    
    public Point screenPos = new Point(0, 0); // Screen offset in coordinate system
    public int zoomDivider=1000;  // Coordinate / zoomDivider = pixel
    
    public int frameCounter = 0;
    
    /** Nanoseconds per frame  **/
    public long gameSpeed = 10000000;

    
    public static SingleButtonGame getInstance(){
    	return me;
    }
    
    private SingleButtonGame(String txt) {
    	
    	super(txt);
    	
        this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

        setUndecorated(true);
        setBounds(0,0,screenSize.width, screenSize.height);      
        
        ImageHandler.getInstance();
        SpriteHandler.getInstance();
    	
    	this.add(spelyta);
    	th = new Thread(this);
		th.start();

		addKeyListener( this );
		addMouseListener( new OBCMouseListener() );
		initBasics();
		mainMenu = new MainMenu();
		configurationMenu = new ConfigurationMenu();
		//initBasics();
    }
    
    
    public static void main(String[] args){
    	
    	me = new SingleButtonGame("Single Button Game");
    	me.setVisible(true);
    	
    }
    
    public void run(){
		Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
		long nextFrameStart = System.nanoTime();
		//PerformanceMonitor.startCheck("Sleep");
		//PerformanceMonitor.stopCheck("Sleep");
		while(true){	
			
			PerformanceMonitor.finishLoop();
			PerformanceMonitor.startCheck("Logic");
			do {
		        switch(gameState){
				case MENU:
					break;
				case CONFIGURATION:
					configurationMenu.tick();
					break;
				case INGAME:
					runGameLogic();
					break;
				}
		        MessageHandler.tick();
		        GTestFrame.getInstance().nextFrame();
		        frameCounter++;
				nextFrameStart += gameSpeed;
		    }while(GameOptions.ensureGameSpeed && nextFrameStart < System.nanoTime());
			PerformanceMonitor.stopCheck("Logic");
			
			PerformanceMonitor.startCheck("Graphics");
			repaint();
			
			PerformanceMonitor.stopCheck("Graphics");
			
			long remaining = nextFrameStart - System.nanoTime();
			
		    if (remaining > 0) {
				try{
					PerformanceMonitor.startCheck("Sleep");
					Thread.sleep(remaining/1000000);
					PerformanceMonitor.stopCheck("Sleep");
					//System.out.println(remaining/1000000);
				}
				catch (InterruptedException ex){
				}
		    }
			Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
		}	
	}
    
    
    
    public void runGameLogic(){ // TODO review carefully
    	for(int n = 0; n < units.size(); n++){
			if (units.get(n) != null && units.get(n).isAlive()){
				units.get(n).nextFrame();
			}
		}
    	
    	
    	
		for(int a = 0; a < attacks.size(); a++){
			if (attacks.get(a) != null)
				attacks.get(a).nextFrame();
		}
		for(int a = 0; a < attacks.size(); a++){
			if (attacks.get(a) != null){
				for(int n = 0; n < units.size(); n++){
					if (units.get(n) != null && units.get(n).isAlive()){
						attacks.get(a).checkHit(units.get(n));
					}
				}
			}
		}
		for(Player p : players) {
			p.nextFrame();
		}
		for(int c=0; c<units.size(); c++){ // full death check, poorly optimized
			if(units.get(c).isDead()) {
				for(Player p : players){
					if(units.get(c) == p.getSelectedUnit()){
						CustomUtil.selectNextUnit(p);
						if(units.get(c) == p.getSelectedUnit() && p.isAlive()){  // no new unit was found -> die
							p.setAlive(false);
							MessageHandler.addMessage("Player "+p.getKey()+" died!", "iconDeath.png");
							if(gameMode.victoryCheck())
								gameState = GameState.VICTORYSCREEN;
							
								
						}
					}
					
				}
			}
		}
		gameMode.onFrameLogic();
		CollisionHandler.handleCollisions();
		
		

		for(int c=0; c<particles.size(); c++){
			particles.get(c).nextFrame(particles);
		}

		repositionScreen();
		
		
		//Particle testing
		/*if(frameCounter%1 == 0)
			particles.add(new ExplosionMainParticle(100000, 100000, 800, 100));
    	*/
    }
    
    private final void repositionScreen(){
    	int topInterfaceHeight=150;
    	int bottomInterfaceHeight=150;
    	int leftInterfaceWidth=100;
    	int rightInterfaceWidth=100;
    	int outerBorder=50000;
    	int minX=0, maxX=0, minY=0, maxY=0;
    	int desiredZoomX, desiredZoomY;
    	int desiredZoom;
    	int desiredScreenX, desiredScreenY;
    	Unit u;
    	boolean firstUnit=true;
	    for(int n = 0; n < units.size(); n++){
			if (units.get(n) != null && units.get(n).isAlive()){
				u=units.get(n);
				if(firstUnit){
					minX=u.getX();
					minY=u.getY();
					firstUnit=false;
				}
				if(minX>u.getX())  minX=u.getX();
				if(minY>u.getY())  minY=u.getY();
				if(maxX<u.getX())  maxX=u.getX();
				if(maxY<u.getY())  maxY=u.getY();
			}
	    }
	    desiredZoomX=(maxX-minX+2*outerBorder)/(screenSize.width-leftInterfaceWidth-rightInterfaceWidth);
	    desiredZoomY=(maxY-minY+2*outerBorder)/(screenSize.height-topInterfaceHeight-bottomInterfaceHeight);
	    if(desiredZoomX > desiredZoomY)
	    	desiredZoom=desiredZoomX;
	    else
	    	desiredZoom=desiredZoomY;
	    
	    int zoomSpeed=3;
	    while(desiredZoom != zoomDivider && zoomSpeed > 0){
		    if(desiredZoom>zoomDivider)
		    	zoomDivider+=1;
		    if(desiredZoom<zoomDivider)
		    	zoomDivider-=1;
		    zoomSpeed--;
	    }
	    
	    //desiredScreenX = minX-outerBorder;
	    //desiredScreenY = minY-outerBorder - topInterfaceHeight*zoomDivider;
	    desiredScreenX = (minX+maxX)/2-(screenSize.width+leftInterfaceWidth-rightInterfaceWidth)*zoomDivider/2;
	    desiredScreenY = (minY+maxY)/2-(screenSize.height+topInterfaceHeight-bottomInterfaceHeight)*zoomDivider/2;
	    screenPos.setLocation(desiredScreenX, desiredScreenY);
	    
    }
    
    /**
     * Inits basic variables that are needed from the start of the program.
     */
    public void initBasics(){
    	GameOptions.availableGameModes.add(new FreeForAll(this));
    	GameOptions.availableGameModes.add(new Tutorial(this));
    	GameOptions.availableGameModes.add(new Survival(this));
    	GameOptions.selectedGameMode = GameOptions.availableGameModes.firstElement();
    }
    
    /**
     * Starts the selection of players and equipment.
     */
    public void initConfiguration(){
    	gameState = GameState.CONFIGURATION;
    	gameMode = GameOptions.selectedGameMode;
    	
    	/*switch(GameOptions.gameMode){
    	case FREE_FOR_ALL:
    		gameMode = new core.gamemodes.FreeForAll(this);
    		break;
    	case SURVIVAL:
    		gameMode = new core.gamemodes.Tutorial(this);
    		break;
    	default:
    		gameMode = new core.gamemodes.FreeForAll(this);
    		break;
    	}*/
    	
    	
    	//ActionSet as = new ActionSet();
    	MessageHandler.showMessage("Press the keys to be used, one key for each player.", "iconInfo.png");
    	MessageHandler.addMessage("Once all players have been chosen, press space to start the game.", "iconInfo.png");
    }
    
    /**
     * Used to start the actual game.
     */
    public void initGame(Vector<PlayerData> playerData){
    	resetAll();
    	CollisionHandler.init(this);
    	if(gameMode.forcedMap == null || !GameOptions.useRecommendedMap)
    		map=new Map(GameOptions.mapName);
    	else
    		map=new Map(gameMode.forcedMap);
    	
    	for(PlayerData data : playerData) {
    		gameMode.createPlayer(data);
    		/*Point spawnPoint = map.getSpawnPoint(playerCounter);
    		Player addedPlayer = new Player(125*playerCounter+80, 100, playerCounter, data.getKey(), spawnPoint, this);
    		addedPlayer.setPlayerData(data);
			players.add(addedPlayer);
			addedPlayer.toMainSpinner();
			addedPlayer.spawnFirstUnit();
			playerCounter++;*/
    		
			//System.out.println(players.get(q).team);
		}
    	System.out.println("We now have "+players.size()+" players");
		
		//generateBasicWalls();
		
		/*for(Player p : players){
			switch(GameOptions.gameMode){
			case SURVIVAL:
				p.team = players.indexOf(p)%1;
				break;
			case TEAMS:
				p.team = players.indexOf(p)%2;
				break;
			case FREE_FOR_ALL:
				break;
			}		
		}*/
		
		
		gameMode.setupAI();
		gameMode.initGame();
		
		gameState = GameState.INGAME;
    }
    
    /**
     * OUTDATED
     * Clears all important variables to be able to start a new round
     */
    private void resetAll(){
		players.clear();
		attacks.clear();
		units.clear();
    }
	
	
	public void paint(Graphics g){ //called with repaint() 
		if (dbImage == null){ //creates an image (like buffer)
			dbImage = createImage (screenSize.width, screenSize.height);
			dbg = dbImage.getGraphics();
		}
		dbg.setColor (Color.white);
		//dbg.fillRect (0, 0, screenSize.width, screenSize.height);
		dbg.setColor (getForeground());
		
		switch(gameState){
		case MENU:
			mainMenu.paint(dbg);
			break;
		case INGAME:
			paintAll(dbg);
			break;
		case CONFIGURATION:
			configurationMenu.paint(dbg);
			break;
		case VICTORYSCREEN:
			paintVictory(dbg);
			break;
		default:
			dbg.setColor(Color.magenta); 
			dbg.drawString("WARNING: Gamestate not supported yet", 100, 100);
		}	
		dbg.setColor(Color.magenta); 
		dbg.setFont(Constants.defaultFont);
		dbg.drawString("Game running at second "+frameCounter*gameSpeed/1000000000, 1000, 70);
		dbg.drawString("Particle count: "+particles.size(), 1000, 85);
		//dbg.drawString(GameOptions.errorString, 1000, 100);
		
		MessageHandler.paint(dbg);
		//paintDebugInfo(dbg);
		PerformanceMonitor.paint(dbg, screenSize.width-200, 0);
		GTestFrame.getInstance().paint(dbg);
		g.drawImage (dbImage, 0, 0, this);
		PerformanceMonitor.finishFrame();
	}
	
	public void paintDebugInfo(Graphics g){
		int y = screenSize.height - 100;
		g.setColor(Color.white);
		g.fillRect(0, y, 200, 100);
		g.setColor(Color.black);
		g.setFont(Constants.defaultFont);
		
		y += 12;
		dbg.drawString("op? "+ isOpaque(), 5, y);
		y += 12;
		dbg.drawString("Image voilatile? "+ImageHandler.getInstance().getImage("iconNext.png").getCapabilities(null).isTrueVolatile(), 5, y);
		y += 12;
		dbg.drawString("g is "+ g.getClass(), 5, y);
	}
	
	public void paintAll(Graphics g){
		
		/*Graphics2D g2d = (Graphics2D)g;
		RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
		rh.put(RenderingHints.KEY_RENDERING,
	               RenderingHints.VALUE_RENDER_SPEED);

	    g2d.setRenderingHints(rh);*/
		
		Graphics2D g2d = (Graphics2D)g;
		g2d.setStroke(new BasicStroke(2));
		//g2d.setStroke(new Stroke());
		
		
		g.fillRect(0, 0, screenSize.width, screenSize.height);
		paintGrid(g);
		
		for(int n = 0; n < units.size(); n++){
			if (units.get(n) != null && units.get(n).isAlive()){
				//units.get(n).paint(g);
				units.get(n).paint(screenPos, zoomDivider, g);
			}
		}
		
		for(Wall w : map.walls){
			w.paint(screenPos, zoomDivider, g);
			
		}
		for(WallGroup wg : map.wallGroups){
			wg.paint(screenPos, zoomDivider, g);
		}
		
		/*try{
			for(Attack a : attacks){
				if (a != null){
					//attacks.get(n).paint(g);
					a.paint(screenPos, zoomDivider, g);
				}
			}
			
		}
		
		catch(ConcurrentModificationException ex){
			System.out.println("WARNING: attack concurrent exception");
		}*/
		for(int c=0; c<attacks.size(); c++){
			if (attacks.get(c) != null){
				//attacks.get(n).paint(g);
				attacks.get(c).paint(screenPos, zoomDivider, g);
			}
		}
		/*for(Particle p : particles){
			p.paint(screenPos, zoomDivider, g);
		}*/
		for(int c=0; c<particles.size(); c++){
			particles.get(c).paint(screenPos, zoomDivider, g);
			
		}
		//g.setColor(Color.white);
		//g.drawString("Particle count:" + particles.size(), 500, 500);
		
		for(Player p : players) {
			p.paint(screenPos, zoomDivider, g);
				
		}

		
	}
	private void paintGrid(Graphics g){ // minor bug when screen coordinates are less than 0.
		Color[] colors = {new Color(20, 20, 20), new Color(30,30,30), new Color(40, 40, 40)};
		
		int row=0, column=0;
		int startRow, startColumn;
		int size = Constants.gridSize;
		startRow=screenPos.y/size+8;
		startColumn = screenPos.x/size+8;
		row=startRow;
		column=startColumn;
		int initialDrawX = -(screenPos.x%size);
		int drawX =  initialDrawX;
		int drawY = -(screenPos.y%size);
		while(drawY/zoomDivider < screenSize.height){
			while(drawX/zoomDivider < screenSize.width){
				int cIndex = row%2+column%2; // incorrect when row or column is less than 0.
				if(cIndex < 0)
					cIndex = -cIndex;
				g.setColor(colors[cIndex]);
				g.fillRect(drawX/zoomDivider, drawY/zoomDivider, size/zoomDivider+1, size/zoomDivider+1);
				drawX = drawX+size;
				column++;
			}
			drawY = drawY+size;
			drawX = initialDrawX;
			column = startColumn;
			row++;
			
		}
		
	}
	
	public void paintVictory(Graphics g){
		Player winningPlayer = CustomUtil.checkVictory(players);
		g.setColor(Color.RED);
		g.fillRect(0, 0, screenSize.width, screenSize.height);
		g.setColor(Color.gray);
		g.fillRect(350, 410, screenSize.width-700, 100);
		g.setColor(winningPlayer.getColor());
		g.setFont(new Font("Monospace", Font.PLAIN, 50));
		g.drawString("Player "+winningPlayer.getKey()+" won!", 480, 480);
	}
	
	/**
	 * Transform from ingame coordinate to screen pixel coordinate
	 */
	private Point onscreenPoint(Point coordinate){
		int x = (coordinate.x-screenPos.x)/zoomDivider;
		int y = (coordinate.y-screenPos.y)/zoomDivider;
		return new Point(x, y);
	}
	
	
	
	public void keyPressed( KeyEvent e ) {
		char c = e.getKeyChar();
		
		if(e.getKeyCode() == KeyEvent.VK_ESCAPE){
			System.exit(0);
		}
		
		switch(gameState){
		case MENU:
			mainMenu.keyPressed(e);
			break;
		case CONFIGURATION:
			configurationMenu.keyPressed(e);
			break;
		case INGAME:
			for(Player p : players) {
				if(c==p.getKey())
					p.activate();
			}
		default:
		}
		
	}
    public void keyReleased( KeyEvent e ) { }
    public void keyTyped( KeyEvent e ) { }
    
    public int emptyAttack(){ //Finds the first null-attack in the attacks-vector
    	for(int c=0; c<attacks.size()+1; c++){
    		if(attacks.get(c)==null){
    			return c;
   			}
    	}
    	return 0;
    } 
	
	public Vector<Unit> getUnits(Player master){
		Vector<Unit> ret = new Vector<Unit>();
		for(Unit u : units) {
			if(u.master==master && !u.isSelectable) {
				ret.add(u);
		}}
		return ret;
	}
	
	public Vector<Player> getPlayers(){
		return players;
	}
	
}

